/**
 * 
 */
package se.cirrus.base.com;


import se.cirrus.base.error.*;

import java.net.*;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.logging.LogManager;
import java.util.logging.Logger;

import se.cirrus.base.struct.*;

/**
 * @author Winquman
 * This is a representation of a remote node.
 */
public class Node {
	
	public static int CONNECTION_BASICIP = 100101;
	public static int CONNECTION_DUMMY = 100102;
	
	public static int NODE_BASIC = 200101;
	public static int NODE_DUMMY = 200102;
	
	private int id;
	public final int nodeType;
	public final int connectionType;
	private Communicator com;
	private String hostname;
	private int lclock;
	private ConcurrentLinkedQueue<Message> mq; // message queue for incoming messages
	private Logger log;
	
	
	
	Node(int nodeType, int connectionType, String nodeAddress) throws CommunicationException  {
		id = -1;
		this.nodeType = nodeType;
		this.connectionType = connectionType;
		hostname = nodeAddress;
		
		log = LogManager.getLogManager().getLogger("ALL");
		log.fine("Initializing node: " + this);
		
		if(nodeType == NODE_BASIC) {
			;
		} else if(nodeType == NODE_DUMMY) {
			;
		}
		else
			throw new IllegalArgumentException("Unable to find node type for: " + nodeType);
		
		if(connectionType == CONNECTION_BASICIP) {
			log.fine("Creating connector for remote host " + hostname);
			com = new BasicIPCommunicator(hostname);
		} else if(connectionType == CONNECTION_DUMMY) {
			;
		}
		else
			throw new IllegalArgumentException("Unable to find connection adapter for: " + connectionType);
		
		try {
			log.fine("Connecting connector for node: " + this);
			com.connect();
			log.fine("Connector connected for node: " + this);
		} catch (InvalidStateException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			throw new CommunicationException("Unable to configure Connector.", e);
		} catch (CommunicationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			throw e;
		}
		
	}
	
	
	public Node(Socket s) {
		nodeType = NODE_BASIC;
		connectionType = CONNECTION_BASICIP;
	}
	
	public int getClock() throws InvalidStateException {
		if(lclock == -1)
			throw new InvalidStateException("Clock unknown for node: " + id);
		else
			return lclock;
	}
	
	public Message getNextIncoming() throws CommunicationException, InvalidStateException {
		Message m;
		m = com.getNext();
		if(m == null) {
			log.finest("No new message for node " + this);
			return null;
		}
		log.fine("New message at node " + this + ": " + m);
		if(this.id == -1) {
			setID(m.getSource());
			log.fine("Setting correct ID for node " + this + " with host " + hostname);
		}
		
		return m;
	}
	
	public void sendMessage(Message m) throws CommunicationException, InvalidStateException {
		log.fine("Sending a message through " + this);
		com.send(m);
	}
	
	public boolean equals(Node n) {
		return (n.id == this.id);
	}
	
	public void setID(int nodeID) throws InvalidStateException {
		if(id == -1)
			id = nodeID;
		else
			throw new InvalidStateException("Node ID already set!");
	}
	
	public int getID() {
		return id;
	}
	
	public String toString() {
		return "Node" + id;
	}
}
